package org.zoomdy.gdbx;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Scanner;

public class Debugger
{
    int debugLevel;
    Process process;
    InputStream inputStream;
    OutputStream outputStream;
    Scanner scanner;
    PrintWriter printWriter;
    String errorMessage;
    
    public Debugger()
    {
        
    }
    
    public int getDebugLevel()
    {
        return debugLevel;
    }

    public void setDebugLevel(int debugLevel)
    {
        this.debugLevel = debugLevel;
    }
    
    public String getErrorMessage()
    {
        return this.errorMessage;
    }

    public boolean open(String gdb, String exec)
    {
        String cmdArray[] =  new String[2];
        cmdArray[0] = gdb;
        cmdArray[1] = "--interpreter=mi";
        
        this.debug(3, cmdArray[0] + " " + cmdArray[1]);
        
        try
        {
            this.process = Runtime.getRuntime().exec(cmdArray);
        } 
        catch (IOException e)
        {
            this.errorMessage = e.getMessage();
        }
        
        if(this.process == null)
        {
            return false;
        }
        
        this.inputStream = this.process.getInputStream();
        this.outputStream = this.process.getOutputStream();
        this.scanner = new Scanner(this.inputStream);
        this.printWriter = new PrintWriter(this.outputStream);
        
        this.waitPrompt();
        
        String response = this.command("-file-exec-and-symbols " + exec);
        if(!response.startsWith("^done"))
        {
            this.errorMessage = response.split("=")[1];
            this.errorMessage = this.errorMessage.substring(1, this.errorMessage.length() - 1);
            return false;
        }
        
        return true;
    }
    
    public void close()
    {
        if(this.process != null)
        {
            this.command("-gdb-exit");
        }
    }
    
    public String command(String cmd)
    {
        String response;
        
        this.printWriter.println(cmd);
        this.printWriter.flush();
        response = this.scanner.nextLine();
        
        this.debug(3, cmd);
        this.debug(3, response);
        
        if(!cmd.equals("-gdb-exit"))
        {
            this.waitPrompt();
        }
        
        return response;
    }
    
    public Long evaluateExpression(String expression)
    {
        Long result = null;
        
        String cmd = "-data-evaluate-expression (int)" + expression;
        String response = this.command(cmd);
        if(response.startsWith("^done"))
        {
            String strResult = response.split("=")[1];
            strResult = strResult.substring(1, strResult.length() - 1);
            result = Long.parseLong(strResult);
            this.debug(2, expression + " = 0x" + Long.toHexString(result));
        }
        
        return result;
    }

    
    public void waitPrompt()
    {
        while(this.scanner.hasNextLine())
        {
            String line = this.scanner.nextLine();
            this.debug(3, line);
            if(line.startsWith("(gdb)"))
            {
                break;
            }
        }
    }
    
    public void debug(int debugLevel, Object obj)
    {
        if(this.debugLevel >= debugLevel)
        {
            System.out.println("<" + debugLevel + ">" + this.getClass().getSimpleName() + ":" + obj);
        }
    }
    
    public static void main(String args[])
    {
        Debugger debugger = new Debugger();
        debugger.setDebugLevel(3);
        debugger.open("i386-elf-gdb", 
                "/home/user/ecos/config/pc_install/tests/net/common/current/tests/tcp_echo");
        debugger.command("-data-evaluate-expression (int)&pthread_create");
        Long result = debugger.evaluateExpression("&idle_thread");
        if(result != null)
        {
            System.out.println("&idle_thread == " + result);
        }
        
        result = debugger.evaluateExpression("&idle_thread->stack_ptr");
        if(result != null)
        {
            System.out.println("&idle_thread->stack_ptr == " + result);
        }
        
        result = debugger.evaluateExpression("&idle_thread->name");
        if(result != null)
        {
            System.out.println("&idle_thread->name == " + result);
        }
        
        result = debugger.evaluateExpression("&idle_thread->list_next");
        if(result != null)
        {
            System.out.println("&idle_thread->list_next == " + result);
        }
        
        debugger.close();
        debugger.waitPrompt();

        try
        {
            debugger.process.waitFor();
        } catch (InterruptedException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("exit code: " + debugger.process.exitValue());
    }
}
